﻿using Core.Tools;
using System;

namespace Core.ObjectOperate.ReflectionImpl
{
    public sealed class PropertyInfo
    {
        public static PropertyInfo Create(System.Reflection.PropertyInfo PropertyInfo, bool NonPublic)
        {
            if (PropertyInfo == null)
            {
                throw new NullReferenceException("PropertyInfo");
            }

            return new PropertyInfo(PropertyInfo, NonPublic);
        }

        private readonly string InternalName;
        private readonly Type InternalType;
        private readonly IDelegate InternalGetValue;
        private readonly IDelegate InternalSetValue;
        private readonly bool GetValueIsPublic;
        private readonly bool SetValueIsPublic;

        private bool InternalCanRead;
        private bool InternalCanWrite;

        private PropertyInfo(System.Reflection.PropertyInfo PropertyInfo, bool NonPublic)
        {
            InternalName = PropertyInfo.Name;
            InternalType = PropertyInfo.PropertyType;

            System.Reflection.MethodInfo TempMethod;

            if (PropertyInfo.CanRead && (TempMethod = PropertyInfo.GetGetMethod(true)) != null)
            {
                InternalGetValue = MethodHelper.CreateDelegate(TempMethod);

                GetValueIsPublic = TempMethod.IsPublic;
            }

            if (PropertyInfo.CanWrite && (TempMethod = PropertyInfo.GetSetMethod(true)) != null)
            {
                InternalSetValue = MethodHelper.CreateDelegate(TempMethod);

                SetValueIsPublic = TempMethod.IsPublic;
            }

            SetNonPublic(NonPublic);
        }

        internal void SetNonPublic(bool NonPublic)
        {
            InternalCanRead = InternalGetValue != null && (NonPublic || GetValueIsPublic);
            InternalCanWrite = InternalSetValue != null && (NonPublic || SetValueIsPublic);
        }

        public string Name { get { return InternalName; } }

        public Type Type { get { return InternalType; } }

        public bool CanRead { get { return InternalCanRead; } }

        public bool CanWrite { get { return InternalCanWrite; } }

        public object GetValue(object Object)
        {
            if (!InternalCanRead)
            {
                throw new ArgumentException("This property no get method.");
            }

            return InternalGetValue.DynamicInvoke(Object, null);
        }

        public void SetValue(object Object, object Value)
        {
            if (!InternalCanWrite)
            {
                throw new ArgumentException("This property no set method.");
            }

            InternalSetValue.DynamicInvoke(Object, null, Value);
        }
    }
}
